# REST framework之分页和视图组件
# 分页
1、看第n页,每页显示n条数据
2、在n个位置,向后查看n条数据
3、加密分页,上一页和下一页
# 简单的返回一张单表
from rest_framework import serializers from api import models class PagerSerializer(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class Pager1View(APIView): def get(self,request,*args,**kwargs): roles = models.Role.objects.all() ser = PagerSerializer(instance=roles, many=True) ret = json.dumps(ser.data) return HttpResponse(ret)
**初步使用渲染器:**之前的数据我们在postman中查看比较麻烦,并且还有json序列化,我们可以初步使用下restframework中的渲染器
1、先注册rest_framework INSTALLED_APPS = [ ... 'rest_framework', ] 2、视图函数中导入Request类,并使用 from rest_framework import serializers from api import models from rest_framework.response import Response class PagerSerializer(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class Pager1View(APIView): def get(self,request,*args,**kwargs): roles = models.Role.objects.all() ser = PagerSerializer(instance=roles, many=True) return Response(ser.data) # url中 url(r'(?P<version>[v1|v2]+)/pager1/$',views.Pager1View.as_view()),
# 小提示:
通过上面的渲染,当我们打开浏览器输入网址后,就会出现restframework渲染后的效果
# 第一种分页:看第n页,每页显示n条数据
# 使用分页类进行分页
from rest_framework import serializers
from api import models
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
class PagerSerializer(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = '__all__'
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
roles = models.Role.objects.all()
# 创建分页对象
pg = PageNumberPagination()
# 在数据库中获取分页数据
page_role = pg.paginate_queryset(queryset=roles,request=request,view=self) # 在全局配置PAGE_SIAE
print(page_role) # [<Role: Role object>, <Role: Role object>, <Role: Role object>]
# 对数据进行序列化
ser = PagerSerializer(instance=page_role, many=True)
return Response(ser.data)
# 配置settings.py文件
REST_FRAMEWORK = { 'DEFAULT_VERSION':'v1', 'ALLOWED_VERSIONS':['v1','v2'], 'VERSION_PARAM':'version', # 允许得key;version=v1 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', 'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser'], 'PAGE_SIZE':3, # 必须配置PAGE_SIZE,否则取出的为None }
# 自定义分页类
class MyPageNumberPagination(PageNumberPagination):
page_size = 2 # 每页显示个数
page_size_query_param = 'size' #配置在url可以接受size的大小的参数:/api/v1/pager1/?page=2&size=4,第二页4条数据
max_page_size = 5 # 表示最大一页的条数,如果size过大对数据库有影响
page_query_param = 'page' # 获取页面的参数
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
roles = models.Role.objects.all()
# 创建分页对象
pg = MyPageNumberPagination()
# 在数据库中获取分页数据
page_role = pg.paginate_queryset(queryset=roles,request=request,view=self) # 在全局配置PAGE_SIAE
print(page_role) # [<Role: Role object>, <Role: Role object>, <Role: Role object>]
# 对数据进行序列化
ser = PagerSerializer(instance=page_role, many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) # 返回的数据中带有上一页和下一页的网址
# 第二种分页:在n个位置,向后查看n条数据
# 自定义偏移分页类
from rest_framework.pagination import LimitOffsetPagination
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2
limit_query_param = 'limit' # 限制的参数
offset_query_param = 'offset' #偏移的参数
max_limit = 5 # /api/v1/pager1/?limit=4&offset=5, 从第4开始取4条,5去不到
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
roles = models.Role.objects.all()
# 创建分页对象
pg = MyLimitOffsetPagination()
# 在数据库中获取分页数据
page_role = pg.paginate_queryset(queryset=roles,request=request,view=self) # 在全局配置PAGE_SIAE
print(page_role)
# 对数据进行序列化
ser = PagerSerializer(instance=page_role, many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data)
# 第三种分页:加密分页,只有上一页和下一页
通过自己的加密规则,你以为下一页是?couser=2吗?答案是不是的!
from rest_framework.pagination import CursorPagination class MyCursorPagination(CursorPagination): cursor_query_param = 'cursor' # url参数 page_size = 2 invalid_cursor_message = ('无效游标') ordering = 'id' # 排序 page_size_query_param = 'size' # 定制每页大小的参数 max_page_size = 5 #/api/v1/pager1/?cursor=cj0xJnA9Mw%3D%3D&limit=4&offset=5 class Pager1View(APIView): def get(self,request,*args,**kwargs): # 获取所有数据 roles = models.Role.objects.all() # 创建分页对象 pg = MyCursorPagination() # 在数据库中获取分页数据 page_role = pg.paginate_queryset(queryset=roles,request=request,view=self) # 在全局配置PAGE_SIAE print(page_role) # [<Role: Role object>, <Role: Role object>, <Role: Role object>] # 对数据进行序列化 ser = PagerSerializer(instance=page_role, many=True) # return Response(ser.data) return pg.get_paginated_response(ser.data)
# 总结
1、如果数据量比较大,如何做分页?
答:通过记录每个分页的最大值和最小值,下一页在最大值基础上偏移多少个数据;但是一般的页码都是通过有规律的数字进行传递,那么存在输入过大页码,所以我们可以对url传递页码的参数进行加密,让用户不知道下一页的页码号
# 视图
简单回顾我们学过的视图类
1、最初版本: View 2、rest_framework中: APIView --> 继承View 3、rest_framework中的更深层视图 GenericAPIView(更大层度的封装了方法) --> 继承APIView 4、rest_framework.viewsets的GenericViewSet --> 继承ViewSetMixin,GenericAPIView GenericAPIView结合ListModelMixin、CreateModelMixin...用来创建、查看等方法 5、rest_framework.viewsets的ModelViewSet --> 继承 class ModelViewSet(mixins.CreateModelMixin, # 增 mixins.RetrieveModelMixin, #获取,需要id mixins.UpdateModelMixin, #更新,需要id mixins.DestroyModelMixin, #删除,需要id mixins.ListModelMixin, # 列出 GenericViewSet) # 针对CreateModelMixin、ListModelMixin使用的url路径不需要传递参数: url(r'(?P<version>[v1|v2]+)/v1/$',views.V1View.as_view({'get':'list', 'post':'create'})), # 针对RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin因为需要传递pk所以,url: url(r'(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$',views.V1View.as_view({ 'get':'retrieve', 'delete':'destroy', 'put':'update', 'patch':'partial_update', })),
# GenericAPIView视图类的使用
from rest_framework.response import Response from rest_framework.generics import GenericAPIView class PagerSerializer(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class V1View(GenericAPIView): queryset = models.Role.objects.all() # 最先获得QuerySet数据 serializer_class = PagerSerializer # 再获得序列化类 pagination_class = PageNumberPagination # 最后获取到分页类 def get(self,request,*args,**kwargs): roles= self.get_queryset() # 自动取上面配置好的queryset pager_roles = self.paginate_queryset(roles) # 找我们写的分页类,然后自动传参进入,返回分页数据 ser = self.get_serializer(instance=pager_roles,many=True) # 序列化 return Response(ser.data)
# GenericViewSet讲解
正如它的名字一样,它只完成一个功能:就是对as_view() 函数进行重写了,里面需要传入一个字典记录不同请求方式所对应的CBV中的触发的视图函数
# 简单实例
from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet class PagerSerializer(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class V1View(GenericViewSet): queryset = models.Role.objects.all() # 最先获得QuerySet数据 serializer_class = PagerSerializer # 再获得序列化类 pagination_class = PageNumberPagination # 最后获取到分页类 def xxx(self,request,*args,**kwargs): roles= self.get_queryset() # 自动取上面配置好的queryset pager_roles = self.paginate_queryset(roles) # 找我们写的分页类,然后自动传参进入,返回分页数据 ser = self.get_serializer(instance=pager_roles,many=True) # 序列化 return Response(ser.data) # 对应的url中 urlpatterns = [ ... url(r'(?P<version>[v1|v2]+)/v1/$',views.V1View.as_view({'get':'xxx',})), ]
# GenericAPIView结合ListModelMixin、CreateModelMixin...用来创建、查看等方法
# views.py文件中 from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin class PagerSerializer(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class V1View(ListModelMixin,CreateModelMixin,GenericViewSet): queryset = models.Role.objects.all() # 最先获得QuerySet数据 serializer_class = PagerSerializer # 再获得序列化类 pagination_class = PageNumberPagination # 最后获取到分页类 # 重点:url.py文件中 urlpatterns = [ ... url(r'(?P<version>[v1|v2]+)/v1/$',views.V1View.as_view({'get':'list','post':'create'})), # 触发list函数,再ListModelMixin中的,根据不同的请方式实现不同的方法... ]
# 最牛逼的ModelViewSet类的使用
#url.py文件中 urlpatterns = [ ... url(r'(?P<version>[v1|v2]+)/v1/$', views.V1View.as_view({'get': 'list', 'post': 'create'})), url(r'(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$', views.V1View.as_view({ 'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update', })), ] # views.py文件中 from rest_framework.viewsets import GenericViewSet,ModelViewSet class PagerSerializer(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class V1View(ModelViewSet): queryset = models.Role.objects.all() # 最先获得QuerySet数据 serializer_class = PagerSerializer # 再获得序列化类 pagination_class = PageNumberPagination # 最后获取到分页类
小提示:
1、以上代码我们就可以实现增删改查了,就只有四行代码 2、总结: a. 增删改查 ModelViews b. 增删: CreateModelMixin、DestroyModelMixin c. 复杂的逻辑: GenericViewSet、APIView
# 使用Serializer类来实现增删改查
models.py文件中
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32, ) class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=6, decimal_places=2) pub_date = models.DateTimeField(default='2019-01-01 10:10:10') pub = models.ForeignKey('Publisher', on_delete=models.CASCADE) authors = models.ManyToManyField('Author')
serializer.py文件中
from rest_framework import serializers from app01 import models class PublisherSerializer(serializers.Serializer): # 序列化Publisher表 name = serializers.CharField() class AuthorSerializer(serializers.Serializer): # 序列化Auhor表 id = serializers.IntegerField() name = serializers.CharField() class BookSerializer(serializers.Serializer): # 序列化Book表 title = serializers.CharField() price = serializers.DecimalField(max_digits=6, decimal_places=2) pub_date = serializers.DateTimeField() pub = PublisherSerializer(required=False, read_only=True) # 表示此字段再post情况下非必填,且自读 authors = serializers.SerializerMethodField(read_only=True) # get_字段名,自读自定义函数 post_pub = serializers.IntegerField(write_only=True) # 只写 post_author = serializers.ListField(write_only=True) # 只写列表 def get_authors(self, obj): ser_obj = AuthorSerializer(obj.authors.all(), many=True) return ser_obj.data def create(self, validated_data): book_obj = models.Book.objects.create( title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], pub_id=validated_data['post_pub'] ) book_obj.authors.set(validated_data['post_author']) return book_obj def update(self, instance, validated_data): instance.title = validated_data.get('title',instance.title) instance.price = validated_data.get('price',instance.price) instance.pub_date = validated_data.get('pub_date',instance.pub_date) instance.pub_id = validated_data.get('post_pub',instance.pub_id) instance.save() instance.authors.set(validated_data.get('post_authors',instance.authors.all())) return instance
对应的views.py
from django.core import serializers from rest_framework.views import APIView from rest_framework.response import Response from app01.serializer import BookSerializer class BookListView(APIView): def get(self, request, *args, **kwargs): # 显示和新建一个url,因为不用pk all_books = models.Book.objects.all() ser_obj = BookSerializer(all_books, many=True) return Response(ser_obj.data) def post(self, request, *args, **kwargs): ser_obj = BookSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) return Response(ser_obj.errors) class BookView(APIView): # 获取单条数据/更新/删除得另一个url def get(self, request, pk, *args, **kwargs): book_obj = models.Book.objects.filter(pk=pk).first() ser_obj = BookSerializer(book_obj) return Response(ser_obj.data) def put(self, request, pk, *args, **kwargs): book_obj = models.Book.objects.filter(pk=pk).first() ser_obj = BookSerializer(book_obj, data=request.data, partial=True) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) return Response(ser_obj.errors) def delete(self, request, pk, *args, **kwargs): obj = models.Book.objects.filter(pk=pk).first() if obj: obj.delete() return Response({'msg': '删除成功'}) return Response({'error': '数据不存在'})
url.py文件
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^books/', views.BookListView.as_view()), url(r'^book/(\d+)/', views.BookView.as_view()), ]